package cn.biplam.front.template.control.rfFlowTable;

import java.util.ArrayList;

import cn.biplam.common.client.OperatorResult;
import cn.biplam.common.tools.TOOLS;
import cn.biplam.common.wrapper.ErrorMessage;
import cn.biplam.common.wrapper.KvMap;
import cn.biplam.common.wrapper.Result;
import cn.biplam.common.wrapper.UserInfo;
import cn.biplam.front.html.HtmlDiv;
import cn.biplam.front.html.HtmlValue;
import cn.biplam.front.html.base.Html;
import cn.biplam.front.html.base.HtmlWindow;
import cn.biplam.front.html.base.Const.Length_Unit;
import cn.biplam.front.html.base.Const.Position_Mode;
import cn.biplam.front.htmlscript.script.FlowTableScript;
import cn.biplam.front.template.base.HiddenParam;
import cn.biplam.front.template.base.Text;
import cn.biplam.front.template.control.rfFlowTable.SummaryRfFlowTable.SummaryValue;
import cn.biplam.front.template.control.rfFlowTable.SummaryRfFlowTable.SummaryValueOpt;
import cn.biplam.front.template.control.rfFlowTable.callback.action.ActionCallback;
import cn.biplam.front.template.control.table.DimTable;
import cn.biplam.front.template.control.table.ElementID;
import cn.biplam.front.template.control.table.Table;
import cn.biplam.front.template.control.table.cell.ButtonCell;
import cn.biplam.front.template.control.table.cell.Cell;
import cn.biplam.front.template.control.table.cell.EmptyCell;
import cn.biplam.front.template.control.table.cell.sum.SummaryCell;
import cn.biplam.front.template.control.table.cell.sum.SummaryTitleCell;

/**
 * 
 * 可反射调用的Flowtable(文字列仅支持一个页面对象,按钮列支持多个页面对象)<BR>
 * 单元格标题通过标题文字Callback获取、单元格动作通过ActionCallback获取
 * @author XiaoRuxing Nov 5, 20098:45:07 PM
 * @version 1.0
 */
public class RfFlowTableImp extends DimTable{

//	/***********************
//	* 表格基本信息
//	***********************/
	/** 内容 */
	private Table table;
	/** 表格宽度 */
	private int tableWidth = -1;
	/** 表格高度 */
	private int iOutHeight = -1;
	/** 表格属性 */
	private RfFlowTablePropertys property;

//	/***********************
//	* 分页信息
//	***********************/
	/** 分页对象ID */
	private String pageCompId;
//	/***********************
//	* 表格行信息
//	***********************/
	/** 存放行刷新动作ID容器的页面ID */
	private static String refreshAidCont="_refAidCon";
	/** 行刷新动作ID */
	private String refreshAid;
	/** 表主键 */
	private String mainKey;

	/** 表格绑定数据集为空时的提示信息 */
	private String emptyInfo = "&nbsp;";
	/** 如果有标题行则为1,
	 * 否则为0 */
	private int startRowIndex = 1;

//	/***********************
//	* 表格维度信息
//	***********************/
	/** 维度列列表 */
	private ArrayList<String>  dimensionField;
	/** 统计列列表 */
	private ArrayList<String>  indicateField;
	/** 维度值列表 
	 *  <维度字段名称,<维度值>>
	 * */
	private KvMap<String,ArrayList<String>> demesionValueList;
	/**  数组格式为:<维度字段名称,维度字段的值列表(多个值用逗号隔开,不含单引号) */
	private KvMap<String, String> demesionValues ;
	/** 
	 *  汇总行列表<BR>
	 *  汇总维度记录ID(当前维度及所有上级维度ID)<BR>
	 *  当前行Index,统计标题列Index<BR>
	 *  */
	private ArrayList<SummaryRowIndex> sumarrayRowIndex;
	/** 当前行维度值 */
	private ArrayList<String> curRowDemesionValue;
	/** 前一行维度值 */
	private ArrayList<String> preRowDemesionValue;
	/** 汇总数据集
	 * 键值对数据格式<维度主键(当前粒度所有维度值合并),<指标名称,统计值>>
	 *  */
	private SummaryValueOpt summaryResult;
	/** 小计行标题 */
	private static final String summaryTitle = "小计:";

	/** 是否已经遍历并绘制表格数据 */
	private boolean haveBeanProcessed = false;

	/**
	 * 创建数据查看表
	 * @param sHomeUrl 应用系统路径
	 * @param mUser    用户信息
	 * @param pageID   表格页面ID
	 * @param mainKey  主键字段名称
	 * @param rs       需要绑定的数据集
	 * @throws ErrorMessage 
	 */
	public RfFlowTableImp(String sHomeUrl,
			UserInfo mUser,
			String pageID,
			OperatorResult rs,
			RfFlowTablePropertys property) throws ErrorMessage{
		super(sHomeUrl, mUser, pageID,rs);
		this.table=new Table(pageID);
		this.property = property;
		this.colFieldName = this.property.colFieldName;
		this.demesionValueList = new KvMap<String, ArrayList<String>>();
		this.sumarrayRowIndex = new ArrayList<SummaryRowIndex>();
	}

	/**
	 *  设置主键字段名称
	 * @param mainKey 主键字段名称
	 */
	protected void setMainKey(String mainKey){
		this.mainKey = mainKey;
	}

	/**
	 *  设置当前表格的记录ID
	 * @param recordId 记录ID
	 */
	protected void setResordId(String recordId){
		this.recordId = recordId;
	}

	/**
	 *  设置表格行刷新动作
	 * @param sRefreshAction 行刷新动作
	 */
	public void setColRefreshAction(String sRefreshAction){
		this.refreshAid = sRefreshAction;
	}


	/**
	 *  设置是否为维度字段
	 * @param sColFieldName 维度字段名称
	 * @throws ErrorMessage 
	 */
	protected void setIsDimensionField(String sColFieldName) throws ErrorMessage{
		if(this.dimensionField==null)
			this.dimensionField = new ArrayList<String>();
		this.dimensionField.add(sColFieldName);
		this.setRemoveRepeatCol(sColFieldName);
	}

	/**
	 *  设置是否为统计字段
	 * @param sColFieldName 维度字段名称
	 * @throws ErrorMessage 
	 */
	protected void setIsIndicateField(String sColFieldName) throws ErrorMessage{
		if(this.indicateField==null)this.indicateField=new ArrayList<String>();
		this.indicateField.add(sColFieldName);
	}

	/**
	 * 获取指定列是否不显示重复数据
	 * @param colName 列名
	 * @return 是否不显示重复数据
	 */
	protected boolean hiddenRepeat(String colName){
		return this.hiddenRepeatData.contains(colName);
	}

	/**
	 * 获取指定列是否维度列
	 * @param colName 列名
	 * @return 是否维度列
	 */
	protected boolean isDemension(String colName){
		if(this.dimensionField==null)
			return false;
		else
			return this.dimensionField.contains(colName);
	}

	/**
	 *  根据维度字段在维度中的索引返回维度字段在表格中的列索引
	 * @param demesion
	 * @return 维度字段在表格中的列索引
	 */
	protected int getDemesionColIndex(int demesion){
		String demesionIndex = this.dimensionField.get(demesion);
		return this.colFieldName.indexOf(demesionIndex);
	}

	/**
	 * @return 维度字段列表
	 */
	protected ArrayList<String> dimensionList(){
		return this.dimensionField;
	}

	/**
	 * @return 统计字段列表
	 */
	protected ArrayList<String> indicateList(){
		return this.indicateField;
	}

	/**
	 *  
	 * @return 是汇总模式表格
	 */
	protected boolean isSummaryTab(){
		return !(this.dimensionField==null || 
				this.dimensionField.size()<1);
	}

	/**
	 *  设置高度
	 * @param height 高度
	 */
	protected void setHeight(int height){
		this.iOutHeight = height;
	}

	/**
	 * 设置宽度
	 * @param width 宽度
	 */
	protected void setWidth(int width) throws ErrorMessage{
		this.tableWidth = width;
		this.table.setWidth(String.valueOf(width));
	}

	/**
	 * @return 获取宽度
	 */
	protected int getWidth(){
		return this.tableWidth;
	}

	/**
	 *  设置汇总数据前处理<BR>
	 *  绘制报表,保存维度值列表<BR>
	 *  保存当前粒度值汇总对应的行和列索引<BR>
	 *  汇总时要求清单数据集按照维度排序
	 * @throws ErrorMessage 
	 */
	protected void processed() throws ErrorMessage{
		this._init();
		this.haveBeanProcessed = true;//设置表格数据已经被遍历,在get表格时不用在绘制数据行
	}

	/**
	 *  需要先执行this.processed()才能获取到维度值列表
	 * @return 获取维度值列表(有序的按维度粒度排列,最前面为最粗粒度维度)<BR>
	 *         数组格式为:<维度字段名称,维度字段的值列表(多个值用逗号隔开,不含单引号)>
	 * @throws ErrorMessage 
	 */
	protected  KvMap<String, String> getDemesionValueList() throws ErrorMessage{
		if(this.demesionValues==null){
			demesionValues = new KvMap<String, String>();
			ArrayList<String> values = null;
			for(String demesionField:this.dimensionList()){
				values = this.demesionValueList.getByKey(demesionField);//当前维度的所有值
				demesionValues.put(demesionField, TOOLS.listToString(values));
			}
		}
		return demesionValues;
	}

	/**
	 *  设置汇总数据集<BR>
	 * @param result 汇总数据集<BR>
	 *               所有粒度下的指标汇总<BR>
	 */
	protected void setSummaryResult(SummaryValueOpt result){
		this.summaryResult = result;
	}

	/**
	 * @return 表格页面
	 * @throws ErrorMessage
	 */
	protected HtmlWindow get() throws ErrorMessage{
		if(!haveBeanProcessed){
			this.processed();
		}else{
			this._createSummaryRow();//创建汇总行
		}
		//创建分页组件
		HtmlWindow page = this.getPageProperty();

		//设置表格边框
		HtmlWindow tb = this.table.get();
		tb.setStyle("overflow-y", "auto");//设置纵向滚动条
		
		if(this.tableWidth>0){//如果设置了宽度设置表格宽度
			tb.setStyleWidth(this.tableWidth);
		}
		tb.bind("rflowTableWidthResize('"+tb.getID()+"',"+this.tableWidth+",'0');");
		//设置纵向滚动时标题行不滚动
		HtmlWindow title = table.getRow(0);
		title.setPositionMode(Position_Mode.Relative);
		title.setTop("expression(this.offsetParent.scrollTop)",Length_Unit.empty);
		//设置高度
		if(this.iOutHeight>0){
			tb.bind("rflowTableResize('"+tb.getID()+"',"+this.iOutHeight+",'0');");
		}
		//设置分页组件宽度等于表格宽度
		if(this.pageCompId!=null){
			tb.bind(FlowTableScript.setPageCompSize(tb.getID(), this.pageCompId));
		}

		HtmlWindow outTable = new HtmlDiv(this.outerContainerId);
		outTable.put(tb);
		//设置表格容器宽度等于表格宽度
		if(page!=null){
			outTable.put(page);
		}
		return outTable;
	}

	/**
	 *  初始化
	 */
	private void _init() throws ErrorMessage{
		//创建标题行
		if(property.isHaveTitle()){
			RfFlowTableColCreater.createTableTitle(property, table);
			this.startRowIndex = 1;
		}else{
			this.startRowIndex = 0;
		}
		//创建表格内容
		this._setContent();
		//设置行刷新动作
		this._setRefreshAct();
		//将当前表格记录ID放入隐藏容器
		if(this.recordId!=null)
			this.table.putExtend(new HtmlValue(this.pageID+"r",null,recordId));
	}

	/**
	 * 依次读取数据集行数据创建表格内容
	 */
	private void _setContent() throws ErrorMessage{
		if(TOOLS.exist(rs)){
			Result result = rs.getResult();
			result.beforeFirst();//游标放到第一行
			int rowIndex = startRowIndex;//行索引,第一行为标题行
			while(result.next()){
				_createRow(result, rowIndex);//创建行内容
				rowIndex++;
			}
			//页面最后创建所有维度的汇总行
			_createPageLastSummaryRow();
		}else{
			createNull();//数据集为空时放入空行
		}
	}

	/**
	 * 创建行内容 
	 */
	private void _createRow(Result result,int rowIndex) throws ErrorMessage{
		String fieldName;//当前列字段名称
		String rowId = new ElementID(this.pageID, rowIndex).toString();;//行ID
		String keyValue = (this.mainKey==null)?null:rs.getValue(this.mainKey);//主键值,如果主键字段名为空则主键值为空
		HtmlWindow colContent;//当前列内容
		int colIndex = 0;
		//如果是汇总模式创建当前行维度数据容器
		if(this.isSummaryTab()){
			this.curRowDemesionValue = new ArrayList<String>();
		}
		//逐列创建行内容
		for(colIndex=0;colIndex<property.getColCount();colIndex++){
			//获取列名称
			fieldName = property.getFieldName(colIndex);
			//创建单元格内容
			colContent = _createCol(fieldName, 
					result, rowId, keyValue, colIndex, rowIndex);
			//在第一列放入行记录ID
			if(colIndex==0)colContent.put(this._getRowHiddenKey(rowId, keyValue));//主键值为空时放入空
			this.table.put(colContent, rowIndex, colIndex);
		}
		//设置汇总行
		if(this.isSummaryTab()){
			_setDimensionIndex(rowIndex);
			this.preRowDemesionValue = new ArrayList<String>(this.curRowDemesionValue);
		}
		//创建按钮列
		_createOperatorCol(result, keyValue, rowId, rowIndex, colIndex);
	}

	/**
	 *  创建单元格
	 */
	private HtmlWindow _createCol(String fieldName, 
			Result result,
			String rowId, 
			String keyValue,
			int colIndex,
			int rowIndex) throws ErrorMessage{
		HtmlWindow colContent;
		//获取当前列动作
		ActionCallback action = property.getFieldActionCallback(fieldName);
		if(action!=null && this.mainKey==null)//当前列有动作,但是未设置主键字段
			throw new ErrorMessage("Main Key Field must be set in the RfFlowTable!");
		//创建当前列
		//如果需要汇总,当前列是维度列将当前数据放入维度数据列表
		if(this.isDemension(fieldName)){
			saveDimensionList(fieldName, rowIndex, colIndex, result);
		}
		colContent = this.createCol(
				sHomeUrl, 
				mUser, 
				property.getFieldCotentCallback(fieldName), //内容回调器
				action, //动作回调
				rowId,  //行容器ID
				keyValue, 
				result,
				colIndex,
				fieldName);
		return colContent;
	}

	/**
	 *  保存维度值列表
	 */
	private void saveDimensionList(String fieldName,
			int rowIndex,
			int colIndex,
			Result result) throws ErrorMessage{
		ArrayList<String> value = this.demesionValueList.getByKey(fieldName);
		if(value==null){
			value = new ArrayList<String>();
			this.demesionValueList.put(fieldName, value);
		}
		String contentValue = result.getString(fieldName);
		if(!value.contains(contentValue)){
			if(contentValue!=null){
				value.add(contentValue);
			}
		}
		//当前行维度值
		if(contentValue!=null)
			this.curRowDemesionValue.add(contentValue);
	}

	/**
	 *  设置汇总行位置
	 * @param rowIndex
	 * @throws ErrorMessage 
	 */
	private void _setDimensionIndex(int rowIndex) throws ErrorMessage{
		if(this.preRowDemesionValue==null)return;
		ArrayList<String> curDim = new ArrayList<String>(this.curRowDemesionValue);
		ArrayList<String> preDim = new ArrayList<String>(this.preRowDemesionValue);
		while(true){
			//当前行维度和上一行一致,不添加汇总行
			if(curDim.equals(preDim)){
				return;
			}
			if(curDim.size()==0 || preDim.size()==0)return;
			if(curDim.size()<preDim.size()){
				//如果当前行的维度比上一行的维度级别高,直接汇总上一行的当前粒度
				createSummary(preDim, rowIndex);
				//上一行维度向上一级
				preDim.remove(preDim.size()-1);
				continue;
			}else if(curDim.size()>preDim.size()){//当前行粒度比上一行低
				//转到与上一行相同的粒度比较
				curDim = TOOLS.subList(curDim, 0, preDim.size());
				continue;
			}else{
				//当前维度和上一行维度不一致,添加上一维度的汇总行
				createSummary(preDim, rowIndex);
			}
			//当前行和上一行维度向上一级
			preDim.remove(preDim.size()-1);
			curDim.remove(curDim.size()-1);
		}
	}

	/**
	 *  创建汇总行
	 */
	private void createSummary(ArrayList<String> dimension,
			int rowIndex){
		int dimIndex = this.getDemesionColIndex(dimension.size()-1);
		String colName = this.dimensionList().get(dimIndex);
		SummaryRowIndex sumarry = new SummaryRowIndex();
		sumarry.demesion = new ArrayList<String>(dimension);
		sumarry.rowIndex = rowIndex;
		sumarry.colIndex = property.getColNumb(colName);//最后一列维度的下标
		this.sumarrayRowIndex.add(sumarry);
	}

	/**
	 *  创建页面最后汇总行<BR>
	 *  在每页的最后创建维度汇总
	 */
	private void _createPageLastSummaryRow(){
		if(!this.isSummaryTab())return;//非汇总模式不创建
		ArrayList<String> lastRowDemesion = new ArrayList<String>(curRowDemesionValue);
		int i=0;
		//在页面最后创建所有维度的汇总行(如果维度的汇总行不存在)
		while(lastRowDemesion.size()>0){
			SummaryRowIndex summary = new SummaryRowIndex();
			summary.demesion = new ArrayList<String>(lastRowDemesion);
			if(!this.sumarrayRowIndex.contains(summary)){
				summary.colIndex = this.getDemesionColIndex(summary.demesion.size()-1);
				summary.rowIndex = this.table.getRowNumb()+i;
				this.sumarrayRowIndex.add(summary);
				lastRowDemesion.remove(lastRowDemesion.size()-1);//删除最后一个维度值
			}
		}
	}

	/**
	 *  创建汇总行
	 * @throws ErrorMessage 
	 */
	private void _createSummaryRow() throws ErrorMessage{
		if(!this.isSummaryTab() || this.summaryResult == null
				|| this.sumarrayRowIndex == null)return;
		SummaryRowIndex summary = null; 
		for(int i=0;i<this.sumarrayRowIndex.size();i++){
			summary = this.sumarrayRowIndex.get(i);
			summary.rowIndex = summary.rowIndex+i;//调整汇总行的行索引加上前面的索引行数
			//获取当前维度的统计值
			SummaryValue curSummary = this.summaryResult.search(summary.demesion);
			if(curSummary == null)return;//汇总行无值,不绘制
			//插入当前统计行
			this._createEmptyRow(summary.rowIndex,summary.colIndex);
			_createSummaryCol(summary.rowIndex, summary.colIndex, summaryTitle,true);
			//插入统计值
			int j=0;
			int colIndex = -1;
			for(String indicateField:this.indicateList()){
				colIndex = this.property.getColNumb(indicateField);//统计列列序号
				_createSummaryCol(summary.rowIndex, colIndex, 
						String.valueOf(curSummary.indicateValue.get(j))//当前粒度统计值
						,false
				);
				j++;
			}
		}
	}

	/**
	 *  创建汇总单元格
	 */
	private void _createSummaryCol(int rowIndex,
			int colIndex,
			String content,boolean isTitle) throws ErrorMessage{
		HtmlWindow win = null;
		if(content!=null){
			Text text = new Text(content);
			win = text.get();
		}else{
			win = new HtmlDiv();
		}
		Cell cell = null;
		if(isTitle){
			cell = new SummaryTitleCell();
		}else{
			cell = new SummaryCell();
		}
		cell.put(win);
		this.table.replaceCol(cell.get(),rowIndex,colIndex);
	}

	/**
	 *  创建空行
	 * @param rowIndex
	 * @throws ErrorMessage 
	 */
	private void _createEmptyRow(int rowIndex,int colIndex) throws ErrorMessage{
		this.table.insertRow(rowIndex);
		int i = 0;
		for(;i<colIndex;i++){
			this.table.put(new EmptyCell().get(),rowIndex);
		}
		for(;i<this.property.getColCount();i++){
			this.table.put(new SummaryCell().get(),rowIndex);
		}
	}

	/**
	 *  创建操作按钮列
	 */
	private void _createOperatorCol(Result result, String keyValue,
			String rowId,int rowIndex, 
			int colIndex) throws ErrorMessage{
		if(property.getBtCount()<1)return;//如果无按钮返回
		//创建按钮列
		String btCaption;
		ButtonCell btCell= new ButtonCell();
		HtmlWindow curBt;
		for(int i=0;i<property.getBtCount();i++){
			btCaption = property.getBtCaption(i);	//获取按钮标题
			//获取当前按钮动作
			ActionCallback action = property.getBtActCallback(btCaption);
			if(action!=null && this.mainKey==null)//当前列有动作,但是未设置主键字段
				throw new ErrorMessage("Main Key Field must be set in the RfFlowTable!");
			//创建按钮
			curBt = RfFlowTableColCreater.createOperatorCol(
					sHomeUrl, 
					mUser, 
					btCaption, //按钮标题
					action, //动作回调器
					property.getBtCompareCallback(btCaption), //比较回调器
					property.getBtContentCallback(btCaption),//行标题回调器
					rowId,                                //行容器ID
					keyValue, 
					result);
			btCell.put(curBt);
		}
		this.table.put(btCell.get(), rowIndex, colIndex);
		this.table.setColWidth(TOOLS.intToString(btCell.getLength()), rowIndex, colIndex);
	}

	/**
	 * 设置分页属性
	 * @throws ErrorMessage 
	 */
	private HtmlWindow getPageProperty() throws ErrorMessage{
		HtmlWindow pg = null;
		pg = this.getPageProperty(rs, outerContainerId, pageID, tableWidth, table);
		if(pg!=null){
			this.pageCompId = pg.getID();
		}
		return pg;
	}

	/**
	 *  创建空行
	 * @throws ErrorMessage
	 */
	private void createNull() throws ErrorMessage{
		this.table.put(
				new HtmlDiv(null,null,this.emptyInfo),
				1,
				_getColCount());
	}

	/**
	 *  获取当前表格列数
	 */
	private String _getColCount(){
		int colSpan = property.getColCount();//空行所占列数为字段列数
		if(property.getBtCount()>0)colSpan=colSpan+1;//如果存在操作列,则列数加1
		return TOOLS.intToString(colSpan);
	}

	/**
	 * 根据主键值，生成指定行的主键隐藏对象
	 * @param rowID 行ID
	 * @param keyValue 主键值
	 */
	private Html _getRowHiddenKey(String rowID,String keyValue){
		HtmlValue hiddenKey=new HtmlValue(rowID+"r",null,keyValue);
		return hiddenKey;
	}

	/**
	 *  设置表格行刷新动作ID
	 * @throws ErrorMessage 
	 */
	private void _setRefreshAct() throws ErrorMessage{
		if(this.refreshAid==null)return;//如果行刷新动作ID为空不设置行刷新容器
		HiddenParam hp = new HiddenParam(this.pageID+refreshAidCont,this.refreshAid);
		this.table.putExtend(hp.get());
	}

	/**
	 *  汇总行坐标<BR>
	 *  汇总维度记录ID(当前维度及所有上级维度ID)<BR>
	 *  当前行Index,统计标题列Index<BR>
	 */
	class SummaryRowIndex{
		ArrayList<String> demesion;//维度值列表(汇总值获取关键字)
		int rowIndex;//行坐标
		int colIndex;//列坐标
		KvMap<String, String> summaryValue;//当前汇总维度汇总值
		//两个汇总行维度是否相等
		protected boolean equals(SummaryRowIndex row){
			if(row.demesion==null)return false;
			return row.demesion.equals(this.demesion);
		}
	}
}
